﻿using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using Library.Winform.Draw.Tools;
using System;
using System.Collections;
using Library.Util;
using System.Reflection;
namespace Library.Winform.Draw {
	public class GraphicsCollection : ISerializeDraw {
		private UInt16 _maxGroupId;
		public UInt16 MaxGroupId { get { return _maxGroupId; } }

		private void EnsureGroupId() {
			if (MaxGroupId == UInt16.MaxValue) {
				UInt16 groupId = 1;
				var hashGroups = new Hashtable();
				foreach (var objectDraw in _drawList) {
					if (objectDraw.HasGroup) {
						var groups = (List<ObjectDraw>)hashGroups[objectDraw.GroupId];
						if (groups == null) {
							groups = new List<ObjectDraw>();
							hashGroups.Add(objectDraw.GroupId, groups);
						}
						groups.Add(objectDraw);
						if (groups.Count == 1) {
							objectDraw.GroupId = groupId;
							groupId++;
						} else {
							var first = groups[0];
							objectDraw.GroupId = first.GroupId;
						}
					}
				}

				//increase groupid
				groupId++;
				if (groupId == UInt16.MaxValue) {
					throw new OverflowException("Not supported number of group over " + UInt16.MaxValue + " group");
				}
				_maxGroupId = groupId;
			}
		}
		protected readonly IDrawContainer DrawContainer;

		public GraphicsCollection(IDrawContainer drawContainer) {
			DrawContainer = drawContainer;
			_drawList = new List<ObjectDraw>();
		}

		private readonly List<ObjectDraw> _drawList;

		#region Implementation of IStreamSerialization

		public void ReadFrom(Stream stream) {
			_drawList.Clear();
			var assembly = Assembly.GetExecutingAssembly();
			stream.Read(out _maxGroupId);
			UInt16 count;
			stream.Read(out count);
			for (int i = 0; i < count; i++) {
				string typeName;
				stream.ReadAnsi(out typeName);
				var objectDraw = (ObjectDraw)assembly.CreateInstance(typeName);
				// ReSharper disable PossibleNullReferenceException
				objectDraw.ReadFrom(stream);
				// ReSharper restore PossibleNullReferenceException
				_drawList.Add(objectDraw);
			}
		}

		public void WriteTo(Stream stream) {
			stream.Write(_maxGroupId);
			stream.Write((UInt16)_drawList.Count);
			foreach (var objectDraw in _drawList) {
				stream.WriteAnsi(objectDraw.GetType().FullName);
				objectDraw.WriteTo(stream);
			}
		}

		#endregion


		#region Implementation of IDraw

		public void Draw(Graphics g) {
			for (int i = _drawList.Count - 1; i >= 0; i--) {
				var draw = _drawList[i];
				draw.Draw(g);
			}
		}

		#endregion

		/// <summary>
		/// Returns INumerable object which may be used for enumeration
		/// of selected objects.
		/// </summary>
		/// <value></value>

		internal SelectedDrawEnumerable Selection {
			get {
				var list = new List<ObjectDraw>();
				foreach (var objectDraw in _drawList) {
					if (objectDraw.Selected) list.Add(objectDraw);
				}
				return new SelectedDrawEnumerable(list);
			}
		}

		internal void Select(bool isSelect) {
			foreach (var drawObject in _drawList) {
				drawObject.Selected = isSelect;
			}
		}

		/// <summary>
		/// Select all Drawobject in rectangle (rec with scroll offset)
		/// </summary>
		/// <param name="rectangle"></param>
		internal void SelectInRectangle(Rectangle rectangle) {
			Select(false);
			foreach (IObjectDraw o in _drawList) {
				if (!o.Selected && o.IntersectsWith(rectangle))
					o.Selected = true;
			}
		}

		internal ObjectDraw FindDraw(ScrollMouseEventArgs e) {
			return FindDraw(e.Location);
		}

		internal ObjectDraw FindDraw(Point positionWithScrollOffset) {
			return _drawList.FirstOrDefault(o => o.IntersectsWith(new Rectangle(positionWithScrollOffset, new Size(1, 1))));
		}

		internal void Group(params ObjectDraw[] items) {
			if (items == null || items.Length < 2) return;
			_maxGroupId++;
			EnsureGroupId();
			var groupId = MaxGroupId;
			foreach (var objectDraw in items) {
				objectDraw.GroupId = groupId;
			}
		}


		internal void Add(ObjectDraw item) {
			item.GraphicsContainer = DrawContainer;
			_drawList.Add(item);
		}


		internal void Insert(int index, ObjectDraw item) {
			item.GraphicsContainer = DrawContainer;
			_drawList.Insert(index, item);
		}

		internal bool Remove(ObjectDraw item) {
			item.GraphicsContainer = null;
			return _drawList.Remove(item);
		}

		internal void Clear() {
			_drawList.Clear();
		}

		public int Count {
			get { return _drawList.Count; }
		}


		public ObjectDraw this[int index] {
			get { return _drawList[index]; }
		}
	}
}
